knitr::opts_chunk$set(echo = TRUE)

Important Note

So far I have not yet implemented the workflow to securely host RTutor problem sets on your own web server and use it for teaching.

This vignette only describes ideas how web hosting can be done.

Web hosting vs Solving on Students' Own Computers

In the moment, I use RTutor for more advanced classes and let students solve the RTutor problem sets on their own computer and then submit the solution via Moodle. Alternatively, one could host the problem sets on a web server so that students can solve them with their web browser without any need to install R.

I see the main advantage of web hosting in reducing the work that arises if students have problems setting up their R environment with all required packages. This seems mainly an issue in large introductionary classes. There, you want to give students a smooth and easy experience for their first steps with R and not demotivate them with all the hazzle of installing all necessary software and packages. Also, you don't want your TAs to spend most of their time setting up the computers of those students who don't manage the installation on their own.

On the other hand, for smaller, more advanced classes, I clearly prefer that students manage these issues and solve their problems sets on their own computer using the RStudio interface of RTutor instead of the shiny, but more artificial web interface.

Security

Just hosting RTutor problem sets in the web is no problem with shiny server. The main challenge is to host them securely in the web. If students can run arbitrary R code with priviliged access, they may read sensitive files from your server, implant a virus, delete or corrupt important data or highjack your server for other nasty things.

Overview of Main Components for Securing Web Apps

Note: These security mechanisms are neccessary if you want to host RTutor problem sets on your own server. They are not neccesary if you just distribute the problem sets to your students who solve them on their own computer.

1. Start RAppArmor in global.R to restrict file access

As an outer shell for security, it is strictly recommended to use RAppArmor to limit file access to the minimum that is required to run your problem set and save a user's progress. You should call RAppArmor in your global.R file for your file and set a profile that prevents file access beyond what is neccessary to run the problem set.

# Example code in global.R to set a restricted profile in RAppArmor
library(RAppArmor)
aa_getcon()
if (!aa_is_enabled())
  stop("AppArmor is not enabled.")

# Set profile with limited file access
aa_change_profile("outer-armor-myapp")

Even if you believe that some other security mechanisms of RTutor, like the noeval mode, may perhaps be sufficient to prevent malicious attacks, you should always use RAppArmor as an extra layer to prevent undesired access to your server. I cannot rule out that there are bugs in RTutor that create loopholes for malicious access.

To repeat: Never put an RTutor shiny app on your server without an outer layer of approbriate RAppArmor protection!

2. Separate LoginApp from PSApp

Users first start with LoginApp that then opens a link to the PSApp. Both apps can have two separate RAppArmor profiles. Only in the ps app can the user enter more or less arbitrary code.

To store students' progress and evaluate their performance, we need a password secured login mechanism. While shiny server pro allows you to use well established authentification mechanisms, I also try to include a simple login mechanism for the open version of shiny-server. The details are explained farther below.

The login app needs access to a database in which user names, password hashes and salts are stored. Yet, we want RAppArmor to forbid the ps app any read or write access to this user database. Even if a password is stored as a salted hash, you can crack it, if you run a computer long enough. So nobody should be able to infiltrate in a chunk some clever R code that downloads the stored hashes.

If the two apps are independent shiny apps, we can give different file access with RAppArmor for the apps and exclude PSApp from accessing the data base with user passwords hashes.

The key idea is as follows:

  1. The user logs in with the LoginApp

  2. LoginApp then creates a temporary session key (e.g. HBBWdg32b476THBEDGb4z6dh73dg476r) and stores it in a file HBBWdg32b476THBEDGb4z6dh73dg476r.ses with a name equal to the key.

The session file is stored in a folder to which LoginApp has write access and PSApp has read access. An app's access rights are controlled via RAppArmor. While PSApp can read files from the directory, it has not access to list the existing files in the directory, i.e. it does not know which session files exist.

The temporary session file contains several pieces of information: - The username - the time of creation - possibly some information from session$clientData that may help to identify the client's computer. (To prevent simple login by copying the session key from another computer. It cannot fool more sophisticated attacks)

  1. LoginApp creates and perhaps automatically opens a link to PSApp with an url parameter that contains the session code, e.g. https://www.mypsapp.com?key=HBBWdg32b476THBEDGb4z6dh73dg476r. It then checks whether a session file with the corresponding name HBBWdg32b476THBEDGb4z6dh73dg476r.ses exists, and if it is not older than a specified maximum login time (e.g. 3 hours). If these conditions are not met, access is denied. If access is given PSApp reads the user name, loads the corresponding .ups file and shows the problem set given the state stored in the user's .ups file.

3. Restrictions to evaluation of user code

3.1 noeval mode

If the problem set is shown in noeval mode, no user code is evaluated. This requires that the problem set was created with the option preknit = TRUE so that the knitted sample solution already exists.

The code is only parsed and the unevaluated calls are compared to the sample solution. Also automatic hints do not evaluate the code, but try to give a hint based on the unevaluated studen't expression. If the user's input passes the tests, the preknitted sample solution is shown as output.

3.2 secure.eval with RAppArmor

4. Common problems

Your app runs when starting from RStudio on the server but greys out when accessing from the website

Make sure that you have set all required read and write permissions for the files that the user "shiny" needs to access. I am speaking here of the normal Linux file permissions, not the permissions in the RAppArmor profile. Typically you will run RStudio on the server with a different user, who may have more permissions that the shiny server user.

It seems that you also need to set write permissions to directories. For example, my login app showed a grey screen once when there was no write permission to the /db directory.



skranz/armd documentation built on Sept. 4, 2020, 12:22 p.m.